package com.cryptoneedle.center.source.service.enhance;


import com.cryptoneedle.center.config.helper.ConfigPropertyHelper;
import com.cryptoneedle.center.source.entity.enhance.Dimension;
import com.cryptoneedle.center.source.entity.metadata.Catalog;
import com.cryptoneedle.center.source.entity.metadata.Column;
import com.cryptoneedle.center.source.entity.metadata.Database;
import com.cryptoneedle.center.source.entity.metadata.Table;
import com.cryptoneedle.center.source.enums.DatabaseType;
import com.cryptoneedle.center.source.service.crud.*;
import com.cryptoneedle.center.source.util.OracleUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>description: 源数据源-转换服务 </p>
 *
 * @author CryptoNeedle
 * @date 2025-06-23
 */
@Slf4j
@Service
public class TransService {

    @Resource
    private TransService transService;

    @Autowired
    private FoundDataService select;
    @Autowired
    private AddDataService add;
    @Autowired
    private PatchDataService patch;
    @Autowired
    private SaveDataService save;
    @Autowired
    private DeleteDataService delete;

    @Transactional
    public void calcuteDataDistribute(Catalog catalog, Database database) {
        List<Table> tables = select.tables();
        for (Table table : tables) {
            transService.calcuteDataDistribute(catalog, database, table);
        }
    }

    @Transactional
    public void calcuteDataDistribute(Catalog catalog, Database database, Table table) {
        if (StringUtils.isBlank(table.getActiveDimensionName())) {
            return;
        }

        List<Dimension> dimensions = select.dimensionsEnabled(table);
        if (dimensions.isEmpty()) {
            return;
        }

        // 表预估占用空间 MB
        double estimateMegaBytes = table.getEstimateMegaBytes().doubleValue();
        Long rowCount = table.getRowCount();

        // 是否需要分区
        boolean needPartition = false;
        // 分桶数量
        Integer bucketNum;


        // 原始数压缩率（压缩采用LZ4，实际压缩率约为0.1~0.3）
        double compressRatio = 1 - 0.6;
        estimateMegaBytes = estimateMegaBytes * compressRatio;

        // 1.分区策略
        // 单分区大小不超过50GB
        // 为了方便 1000W~2亿 可不分区，直接分桶
        // 2000W以内禁止使用动态分区（容易创建出大量不使用的分区）
        // 2亿以上必须使用分区分桶
        // 单节点分区数 < 20000

        // 2.分桶策略
        // 单分桶大小在1-10GB（官方建议，实际使用0.5-3GB进行估算）
        // 充分利用机器资源：分桶数量 = BE节点数量 * CPU核数 / 2
        // 禁止采用 AUTO，会产生过多的tablet小文件
        // 如果分桶字段存在30%的数据倾斜，禁止使用Hash，改为RANDOM

        // 3.官方建议
        // 单表大小      官方建议
        // 100M < 500M  4-8
        // 500M < 5GB   6-16
        // 5GB  < 50GB  32
        // 50GB < 500GB 每50GB 16~32
        // 500GB < 5TB  每50GB 16~32

        // 4.特殊场景
        // 时序场景 "compaction_policy"="time_series" 降低写入放大

        // 5.项目策略 T
        // 单表大小      实际采用     采用原因
        // < 100M       1          无需考虑并行性能
        // 100M < 500M  3          兼顾并发数和查询效率
        // 500M < 1GB   4          兼顾并发数和查询效率
        // 1GB  < 3GB   6
        // 3GB  < 5GB   8
        // 5GB  < 10GB  10
        // 10GB < 50GB  GB数量
        // 50GB < 500GB 每50GB GB数量
        // 500GB < 5TB  每50GB GB数量

        // 6.计算方法
        // T = 策略值
        // S = BE节点树 * 磁盘块数(磁盘块大小/50GB)
        // C = BE节点数量 * CPU核数 / 2，为最大分桶数
        // 最终分桶数 P

        // 7.实际计算方法（做了一些简化）
        if (estimateMegaBytes >= 1024 * 50) {
            needPartition = true;
        }
        // 以下不考虑分区
        if (needPartition) {
            // todo 考虑分区
            bucketNum = null;
        } else {
            //int s; // 暂不启用
            int c = 5 * 20 / 2;
            if (estimateMegaBytes < 100) {
                bucketNum = 1;
            } else if (estimateMegaBytes < 500) {
                bucketNum = 3;
            } else if (estimateMegaBytes < 1024) {
                bucketNum = 4;
            } else if (estimateMegaBytes < 1024 * 3) {
                bucketNum = 6;
            } else if (estimateMegaBytes < 1024 * 5) {
                bucketNum = 8;
            } else if (estimateMegaBytes < 1024 * 10) {
                bucketNum = 10;
            } else if (estimateMegaBytes < 1024 * 50) {
                bucketNum = (int) Math.ceil(estimateMegaBytes);
            } else {
                bucketNum = Math.max(Math.max((int) Math.ceil(estimateMegaBytes), c), 64);
            }
        }


        table.setTransBucketCount(bucketNum);
        save.save(table);
    }

    @Transactional
    public void transformDatabase(Catalog catalog, Database database) {
        List<Table> tables = select.tables(database);
        for (Table table : tables) {
            transService.transformTable(catalog, database, table);
        }
    }

    @Transactional
    public void transformTable(Catalog catalog, Database database, Table table) {
        if (!table.getTransTableNameManual() && StringUtils.isNotEmpty(table.getSystemCode())) {
            table.setTransTableName(ConfigPropertyHelper.ods() + "_" + table.getSystemCode() + "_" + StringUtils.lowerCase(table.getTableName()) + "_h");
        }
        if (!table.getTransCommentManual()) {
            table.setTransComment(StringUtils.trimToEmpty(table.getComment()));
        }
        if (table.getTransComment() == null) {
            table.setTransComment("");
        }
        save.save(table);

        List<Column> columns = select.columns(table);
        for (Column column : columns) {
            // 转换列
            transService.transformColumn(catalog, database, table, column);
        }
        save.columns(columns);
    }

    private void transformColumn(Catalog catalog, Database database, Table table, Column column) {
        switch (catalog.getDatabaseType()) {
            case DatabaseType.ORACLE:
                OracleUtil.transformDataType(column);
            case DatabaseType.MYSQL: // todo
            case DatabaseType.POSTGRESQL: // todo
            case DatabaseType.SQLSERVER: // todo
        }

        column.setTransTableName(table.getTransTableName());
        if (column.getTransColumnNameManual()) {
            String columnName = column.getColumnName();
            // 去除开头_
            String transformColumnName = Strings.CI.removeStart(columnName, "_");
            // 去除结尾_
            transformColumnName = Strings.CI.removeEnd(transformColumnName, "_");
            // 去除连续_
            transformColumnName = transformColumnName.replaceAll("_{2,}", "_");
            // 小写
            transformColumnName = StringUtils.lowerCase(transformColumnName);
            column.setTransColumnName(transformColumnName);
        }
        if (column.getTransCommentManual()) {
            column.setTransComment(column.getComment());
        }
        if (column.getTransComment() == null) {
            column.setTransComment("");
        }
        // todo transEstimateBytes
    }


}